/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package samples.tasks.text;

import taskgraph.Config;
import taskgraph.channels.CharChannel;
import taskgraph.ports.CharInputPort;
import taskgraph.ports.CharOutputPort;
import taskgraph.tasks.CompositeTask;
import taskgraph.tasks.text.ReverseLine;
import taskgraph.tasks.text.SortLines;

/**
 * Task for find rhyming words.
 * 
 * This class shows a typical way of writing a reusable composite
 * task, which itself reuses existing tasks. Clients of this class,
 * however, use it as a black-box, without knowing or caring how
 * it is implemented internally.
 *  
 * @author Armando Blancas
 * @see taskgraph.ports.CharInputPort
 * @see taskgraph.ports.CharOutputPort
 */
public class FindRhymes extends CompositeTask {

	/**
	 * To make it easy to establish global settings we get the
	 * channel capacity from the global configuration class.
	 */
	private static final int DEFAULT_CAPACITY = Config.get().channelCapacity();
	
	/**
	 * The port that provides the input:
	 * lines of text each containing a single word.
	 */
	private CharInputPort input;
	
	/**
	 * The port to which the output lines are written.
	 */
	private CharOutputPort output;
	
	/**
	 * This variable takes the global capacity by default but
	 * may later be set as a bean property.
	 */
	private int bufferSize = DEFAULT_CAPACITY;

	/**
	 * This constructor allows creating instances as beans
	 * to be used for some future visual graph editor.
	 */
    public FindRhymes() {
    	// do nothing
    }

	/**
     * Creates a fully configured instance.
     * 
     * @param output The char input port.
     */
    public FindRhymes(final CharInputPort input, final CharOutputPort output) {
    	setInput(input);
    	setOutput(output);
    }

	/**
	 * Gets the buffer size for reading chunks of characters.
	 * 
	 * @return the bufferSize
	 */
	public int getBufferSize() {
		return bufferSize;
	}

    /**
     * Gets the input port.
     * 
	 * @return The char input port.
	 */
	public CharInputPort getInput() {
		return input;
	}
	
    /**
     * Gets the output port.
     * 
	 * @return The char output port.
	 */
	public CharOutputPort getOutput() {
		return output;
	}

	/**
	 * Sets the buffer size for reading chunks of characters.
	 * <p>The argument should not be less than the default. If it is, the
	 * default size is used instead.
	 * 
	 * @param bufferSize the bufferSize to set
	 */
	public void setBufferSize(final int bufferSize) {
		this.bufferSize = 
			(bufferSize < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : bufferSize;
	}

    /**
	 * Sets the input port.
	 * 
	 * @param input The char input port.
	 */
    public void setInput(final CharInputPort input) {
    	if (input == null)
    		throw new IllegalArgumentException("input == null");
 		this.input = input;
	}
    
	/**
	 * Sets the output port.
	 * 
	 * @param output The char output port.
	 */
    public void setOutput(final CharOutputPort output) {
    	if (output == null)
    		throw new IllegalArgumentException("output == null");
 		this.output = output;
	}

	/**
     * Creates a graph that finds rhyming words.
     * 
     * The method is a bit naive: reverse the letters of all words,
     * sort them, and then reverse them back. This way the endings
     * will be the same or similar, as in rhymes.
     * 
     * Note that each of the subordinate tasks will be running
     * concurrently, passing words as lines of text. Depending on 
     * the number of processors available, all this may well be
     * running in parallel.
     * 
     * <p><pre>
     * --input--> [Reverse] --ch1--> [Sort] --ch2--> [Reverse] --output-->
     * </pre>
	 */
	@Override
    protected void setup() {
		CharChannel  ch1 = new CharChannel(bufferSize);
		CharChannel  ch2 = new CharChannel(bufferSize);
		
        add(new ReverseLine(input, ch1.getOutputPort()));
        add(new SortLines(ch1.getInputPort(), ch2.getOutputPort()));
        add(new ReverseLine(ch2.getInputPort(), output));
    }

}
